Coverage Report

Created: 2026-03-18 12:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\bin\dns_stress_test.rs
Line
Count
Source
1
use std::net::SocketAddr;
2
use std::sync::Arc;
3
use std::sync::atomic::{AtomicU64, Ordering};
4
use std::time::{Duration, Instant};
5
use tokio::net::UdpSocket;
6
7
0
fn build_dns_query_a(qname: &str, id: u16) -> Vec<u8> {
8
0
    let mut msg = Vec::with_capacity(512);
9
10
0
    msg.extend_from_slice(&id.to_be_bytes());
11
0
    msg.extend_from_slice(&0x0100u16.to_be_bytes());
12
0
    msg.extend_from_slice(&1u16.to_be_bytes());
13
0
    msg.extend_from_slice(&0u16.to_be_bytes());
14
0
    msg.extend_from_slice(&0u16.to_be_bytes());
15
0
    msg.extend_from_slice(&0u16.to_be_bytes());
16
17
0
    for label in qname.trim_end_matches('.').split('.') {
18
0
        let len = label.len();
19
0
        if len == 0 || len > 63 {
20
0
            continue;
21
0
        }
22
0
        msg.push(len as u8);
23
0
        msg.extend_from_slice(label.as_bytes());
24
    }
25
0
    msg.push(0);
26
27
0
    msg.extend_from_slice(&1u16.to_be_bytes());
28
0
    msg.extend_from_slice(&1u16.to_be_bytes());
29
30
0
    msg
31
0
}
32
33
#[tokio::main(flavor = "multi_thread")]
34
0
async fn main() -> anyhow::Result<()> {
35
    // Usage:
36
    // cargo run --release --bin dns_stress_test -- 127.0.0.1:5353 10 20 0 github.com.
37
    // 1 worker can handle 19 clients approx. (with my computer performance)
38
0
    let args: Vec<String> = std::env::args().collect();
39
0
    if args.len() < 5 {
40
0
        eprintln!(
41
            "Usage: {} <target ip:port> <duration_s> <clients> <sleep_ns> [qname]\n\
42
             Example: {} 127.0.0.1:5353 10 200 0 example.com.",
43
0
            args[0], args[0]
44
        );
45
0
        std::process::exit(2);
46
0
    }
47
48
0
    let target: SocketAddr = args[1].parse()?;
49
0
    let duration_s: u64 = args[2].parse()?;
50
0
    let clients: usize = args[3].parse()?;
51
0
    let sleep_ns: u64 = args[4].parse()?;
52
0
    let qname = args.get(5).map(|s| s.as_str()).unwrap_or("example.com.");
53
54
0
    let sent = Arc::new(AtomicU64::new(0));
55
0
    let send_err = Arc::new(AtomicU64::new(0));
56
57
0
    let start = Instant::now();
58
0
    let deadline = start + Duration::from_secs(duration_s);
59
60
0
    let mut handles = Vec::with_capacity(clients);
61
0
    for client_idx in 0..clients {
62
0
        let sent = sent.clone();
63
0
        let send_err = send_err.clone();
64
0
        let qname = qname.to_string();
65
66
0
        let h = tokio::spawn(async move {
67
0
            let sock = UdpSocket::bind("0.0.0.0:0")
68
0
                .await
69
0
                .expect("bind client socket");
70
0
            sock.connect(target).await.expect("connect client socket");
71
72
0
            let mut id: u16 = (client_idx as u16).wrapping_mul(7919).wrapping_add(1);
73
74
0
            while Instant::now() < deadline {
75
0
                id = id.wrapping_add(1);
76
0
                let q = build_dns_query_a(&qname, id);
77
78
0
                match sock.send(&q).await {
79
0
                    Ok(_) => {
80
0
                        sent.fetch_add(1, Ordering::Relaxed);
81
0
                    }
82
0
                    Err(_) => {
83
0
                        send_err.fetch_add(1, Ordering::Relaxed);
84
0
                    }
85
                }
86
87
0
                if sleep_ns > 0 {
88
0
                    tokio::time::sleep(Duration::from_nanos(sleep_ns)).await;
89
0
                }
90
            }
91
0
        });
92
93
0
        handles.push(h);
94
    }
95
96
0
    let mut last = Instant::now();
97
0
    let mut last_sent = 0u64;
98
0
    let mut last_err = 0u64;
99
100
0
    while Instant::now() < deadline {
101
0
        tokio::time::sleep(Duration::from_secs(1)).await;
102
103
0
        let now = Instant::now();
104
0
        let dt = (now - last).as_secs_f64().max(1e-9);
105
106
0
        let s = sent.load(Ordering::Relaxed);
107
0
        let e = send_err.load(Ordering::Relaxed);
108
109
0
        let ds = s - last_sent;
110
0
        let de = e - last_err;
111
112
0
        println!(
113
            "1s: sent/s={:.0} send_err/s={:.0} (total sent={})",
114
0
            ds as f64 / dt,
115
0
            de as f64 / dt,
116
            s
117
        );
118
119
0
        last = now;
120
0
        last_sent = s;
121
0
        last_err = e;
122
    }
123
124
0
    for h in handles {
125
0
        let _ = h.await;
126
    }
127
128
0
    let elapsed = (Instant::now() - start).as_secs_f64().max(1e-9);
129
0
    let s = sent.load(Ordering::Relaxed);
130
0
    let e = send_err.load(Ordering::Relaxed);
131
132
0
    println!("\n=== RESULT ===");
133
0
    println!("target: {target}, qname: {qname}, clients: {clients}, duration: {duration_s}s");
134
0
    println!("sent: {s} ({:.0}/s)", s as f64 / elapsed);
135
0
    println!("send_err: {e} ({:.0}/s)", e as f64 / elapsed);
136
137
0
    Ok(())
138
0
}